package com.biz.primus.ms.commodity.service.backend;

import com.biz.primus.base.enums.commodity.IStatus;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.base.vo.IdNameVo;
import com.biz.primus.base.vo.PageResult;
import com.biz.primus.commodity.vo.CategoryAndBrandVo;
import com.biz.primus.commodity.vo.CategoryQueryVo;
import com.biz.primus.commodity.vo.DeleteCategoryEventVo;
import com.biz.primus.commodity.vo.SyncCategoryVo;
import com.biz.primus.commodity.vo.backend.*;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.vo.SpringPageVO;
import com.biz.primus.ms.base.vo.SpringPageToSpringPageVO;
import com.biz.primus.ms.commodity.dao.po.Category;
import com.biz.primus.ms.commodity.dao.redis.CategoryRedisDao;
import com.biz.primus.ms.commodity.dao.repository.category.CategoryRepository;
import com.biz.primus.ms.commodity.dao.ro.CategoryRo;
import com.biz.primus.ms.commodity.dao.specification.CategorySpecification;
import com.biz.primus.ms.commodity.event.DeleteCategoryEvent;
import com.biz.primus.ms.commodity.service.AbstractCategoryService;
import com.biz.primus.ms.commodity.trans.Category2CategoryListItemVo;
import com.biz.primus.ms.commodity.trans.Category2CategoryRo;
import com.biz.primus.ms.commodity.trans.Category2UpdateCategoryVo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.biz.primus.commodity.exception.CommodityExceptionType.CATEGORY_NOT_FOUND;

/**
 * 分类 ServiceImpl(后台用)
 *
 * @author 江南
 * @usage
 * @reviewer
 * @since 2016/12/19
 */
@Slf4j
@Service
public class CategoryService extends AbstractCategoryService {

    /**
     * 分类缓存容器
     */
    private static Map<String, Object> CATEGORY_CACHE = Maps.newHashMap();

    /**
     * 后台分类树形结构缓存 key
     */
    private static final String BACKEND_TREE_VIEW_CACHE_KEY = "backend-tree-view";

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private CategoryRedisDao categoryRedisDao;

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void syncCategory(SyncCategoryVo syncCategoryVo) {
        Category category = categoryRepository.findOne(syncCategoryVo.getId());
        if (category == null) {
            category = new Category();
            category.setId(syncCategoryVo.getId());
        }
        CopyUtils.copyProperties(syncCategoryVo, category);
        if (syncCategoryVo.getParentCategoryId()!=null){
            Category parentCategory = categoryRepository.findOne(syncCategoryVo.getParentCategoryId());
            if (parentCategory == null) {
                log.info("syncCategory {} 父分类不存在,开始创建", syncCategoryVo);
                parentCategory = new Category();
                parentCategory.setId(syncCategoryVo.getParentCategoryId());
                parentCategory.setStatus(IStatus.DELETE);
                parentCategory.setName("erp 未上传该父分类");
                categoryRepository.save(parentCategory);
            }
            category.setParent(parentCategory);
        }else {
            log.info("{} 分类没有父分类",syncCategoryVo.getName());
        }
        category.setStatus(syncCategoryVo.getStatus() == 1 ? IStatus.DELETE : IStatus.NORMAL);
        this.saveOrUpdateUsingPo(categoryRepository, categoryRedisDao, category, new Category2CategoryRo());
    }

    @Transactional(rollbackFor = Exception.class)
    public void syncCategories(List<SyncCategoryVo> syncCategoryVos){
        for (SyncCategoryVo index : syncCategoryVos) {
            try{
                this.syncCategory(index);
            }catch (Exception e){
                log.info("同步分类失败 {}",index);
                e.printStackTrace();
            }
        }
    }


    @Transactional
    public CreateCategoryVo createCategory(final CreateCategoryVo vo) {
        // 创建 Category, 并且设置 Id
        Category category = new Category();
        category.setId(idService.getNextId());
        category.fromVo(vo);
        //设置默认排序
        Integer idx = categoryRepository.findMaxIdx(vo.getParentCategoryId());
        idx = idx == null ? 1 : idx + 1;
        category.setIdx(idx);
        category.setSaleChannel(vo.getSaleChannel());
        if (log.isDebugEnabled()) {
            log.debug("vo:{}", vo.toString());
        }

        // 设置父分类
        Category parentCategory = null;
        Long parentCategoryId = vo.getParentCategoryId();
        if (parentCategoryId != null) {
            parentCategory = categoryRepository.findByIdAndDeleteFlag(parentCategoryId, Boolean.FALSE);
            AssertUtils.notNull(parentCategory, CATEGORY_NOT_FOUND);
        }
        if (parentCategory == null) {
            if (log.isDebugEnabled()) {
                log.debug("parent category is null, parentCategoryId is {}", vo.getParentCategoryId());
            }
        }
        category.setParent(parentCategory);
        parentCategory.setName("测试修改父分类名称后级联保存");
        this.saveCategory(category);
        vo.setId(category.getId());
        // 返回之前发送创建分类的事件
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateCategory(final UpdateCategoryVo vo) {
        Category category = categoryRepository.findByIdAndDeleteFlag(vo.getId(), Boolean.FALSE);
        AssertUtils.notNull(category, CATEGORY_NOT_FOUND);
        if (log.isDebugEnabled()) {
            log.debug("vo:{}", vo.toString());
        }
        category.fromVo(vo);

        // 设置父分类
        Category parentCategory = null;
        Long parentCategoryId = vo.getParentCategoryId();
        if (parentCategoryId != null) {
            parentCategory = categoryRepository.findByIdAndDeleteFlag(parentCategoryId, Boolean.FALSE);
        }
        if (parentCategory == null) {
            if (log.isDebugEnabled()) {
                log.debug("parent category is null, parentCategoryId is {}", vo.getParentCategoryId());
            }
        }
        category.setParent(parentCategory);
        this.saveCategory(category);
    }

    @Transactional
    public boolean deleteCategory(Long id) {
        Category category = categoryRepository.findOne(id);
        // 如果被删除的分类不存在, 抛出异常
        AssertUtils.notNull(category, CATEGORY_NOT_FOUND);

        if (Objects.equals(category.getDeleteFlag(), Boolean.TRUE)) {
            return Boolean.TRUE;
        }
        category.setDeleteFlag(Boolean.TRUE);
        this.saveCategory(category);

        // 在函数返回之前发送删除分类事件
        this.publishEvent(new DeleteCategoryEvent(this, new DeleteCategoryEventVo(id)));
        return Boolean.TRUE;
    }

    public SpringPageVO<CategoryListItemVo> findAll(CategoryQueryVo reqVo) {
        Page<Category> categories = categoryRepository.findAll(new CategorySpecification(reqVo), reqVo.toPageRequest());
        return SpringPageToSpringPageVO.of(categories, new Category2CategoryListItemVo());
    }

    public BootstrapTablePageResult<CategoryListVo> findAllCategory(SearchPageVo pageVo) {
        Pageable pageable = new PageRequest(pageVo.getPageIndex() - 1, pageVo.getPageSize(), new Sort(Sort.Direction.DESC, "createTimestamp"));
        Page<Category> pageResult = categoryRepository.findByNameLikeAndDeleteFlag(pageVo.getSearchValue(), Boolean.FALSE, pageable);
        List<CategoryListVo> resultList = this.Category2CategoryListVo(pageResult.getContent());
        BootstrapTablePageResult<CategoryListVo> resp = new BootstrapTablePageResult<>();
        resp.setTotal((int) pageResult.getTotalElements());
        resp.setRows(resultList);
        return resp;
    }

    public CategoryItemVo getCategoryItem(Long categoryId) {
        Category category = categoryRepository.findOne(categoryId);
        CategoryItemVo categoryItemVo = new CategoryItemVo();
        categoryItemVo.setName(category.getName());
        categoryItemVo.setId(String.valueOf(category.getId()));
        return categoryItemVo;
    }

    public UpdateCategoryVo getCategoryById(Long categoryId) {
        Category category = categoryRepository.findOne(categoryId);
        return new Category2UpdateCategoryVo().apply(category);
    }

    @Deprecated
    public Integer getCategoryMaxIdx(Long parentCategoryId) {
        return 0;
    }

    public List<CategoryTreeViewVo> getCategoryTreeViewVos() {
        // 获取所有的未删除的顶级分类列表
        List<Category> categories = categoryRepository.findByParentIsNullAndDeleteFlagOrderByIdx(Boolean.FALSE);
        return this.buildCategoryTreeView(categories);
    }

    public void loadCategory2Cache() {
        List<Category> categories = categoryRepository.findByParentIsNullAndDeleteFlagOrderByIdx(Boolean.FALSE);
        List<CategoryTreeViewVo> categoryTreeViewVos = this.buildCategoryTreeView(categories);

        if (CollectionUtils.isNotEmpty(categories)) {
            if (log.isDebugEnabled()) {
                log.debug("loadCategory2Cache from repository: count {}", categories.size());
            }
            CATEGORY_CACHE.put(BACKEND_TREE_VIEW_CACHE_KEY, categoryTreeViewVos);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("categories in repository are empty");
            }
        }
    }

    /**
     * 递归构造分类树形结构
     *
     * @param categories 分类列表
     * @return 分类树形结构
     */
    private List<CategoryTreeViewVo> buildCategoryTreeView(List<Category> categories) {
        // 如果分类集合为空, 返回空集合
        if (CollectionUtils.isEmpty(categories)) {
            if (log.isDebugEnabled()) {
                log.debug("there is no category");
            }
            return Lists.newArrayList();
        }

        // 如果分类集合不为空, 递归构造
        List<CategoryTreeViewVo> treeViewVos = Lists.newArrayList();
        for (Category category : categories) {
            // 如果分类未被删除
            if (!category.getDeleteFlag()) {
                CategoryTreeViewVo categoryTreeViewVo = new CategoryTreeViewVo();
                categoryTreeViewVo.setId(String.valueOf(category.getId()));
                categoryTreeViewVo.setName(category.getName());
                categoryTreeViewVo.setStatus(category.getStatus());
                if (CollectionUtils.isNotEmpty(category.getChildren())) {
                    List<CategoryTreeViewVo> children = this.buildCategoryTreeView(category.getChildren());
                    categoryTreeViewVo.setChildren(children);
                }
                treeViewVos.add(categoryTreeViewVo);
            }


        }
        return treeViewVos;
    }

    /**
     * 保存排序
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateSort(CategorySortVo vo) {
        if (vo != null && vo.getCategorySortListVos() != null) {
            List<Category> categorys = Lists.newArrayList();
            List<Long> categoryIds = Lists.newArrayList();
            Map<Long, Integer> categoryIdAndIdx = new HashMap<>();
            for (CategorySortListVo index : vo.getCategorySortListVos()) {
                categoryIds.add(index.getId());
                categoryIdAndIdx.put(index.getId(), index.getKuaiheIdx());
            }
            categorys = categoryRepository.findAll(categoryIds);
            saveOrUpdateUsingPos(categoryRepository, categoryRedisDao, categorys, new Category2CategoryRo());
            final List<UpdateCategoryVo> categoryVos = Lists.newArrayList();
            for (Category category : categorys) {
                UpdateCategoryVo updateCategoryVo = new UpdateCategoryVo();
                updateCategoryVo.setId(category.getId());
                categoryVos.add(updateCategoryVo);
            }
        }
    }

    public List<IdNameVo> findCategoryByIds(List<Long> ids) {
        return categoryList2IdNameList(categoryRepository.findByIdInAndDeleteFlag(ids, Boolean.FALSE));
    }

    public PageResult<IdNameVo> findCategoryByName(SearchPageVo searchPageVo) {
        PageRequest pageRequest = new PageRequest(searchPageVo.getPageIndex(), searchPageVo.getPageSize());
        Page<Category> pageCategory = categoryRepository.findByNameLikeAndDeleteFlag("%" + searchPageVo.getSearchValue() + "%", Boolean.FALSE, pageRequest);
        List<IdNameVo> resultList = categoryList2IdNameList(pageCategory.getContent());
        return PageResult.of(searchPageVo.getPageIndex(), searchPageVo.getPageSize(), resultList);
    }

    private List<IdNameVo> categoryList2IdNameList(List<Category> categories) {
        List<IdNameVo> resultList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(categories)) {
            for (Category category : categories) {
                resultList.add(new IdNameVo(String.valueOf(category.getId()), category.getName()));
            }
        }
        return resultList;
    }

    public List<IdNameVo> getSelectCategory() {
        List<Category> selectCategory = categoryRepository.findByDeleteFlag(Boolean.FALSE);
        return categoryList2IdNameList(selectCategory);
    }

    public List<IdNameVo> getTopCategories() {
        List<Category> categories = categoryRepository.findByParentIsNullAndDeleteFlagOrderByIdx(Boolean.FALSE);
        List<IdNameVo> idNameVos = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(categories)) {
            for (Category category : categories) {
                IdNameVo idNameVo = new IdNameVo(String.valueOf(category.getId()), category.getName());
                idNameVos.add(idNameVo);
            }
            return idNameVos;
        }
        return idNameVos;
    }

    private List<CategoryListVo> Category2CategoryListVo(List<Category> inputs) {
        List<CategoryListVo> resultList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(inputs)) {
            for (Category category : inputs) {
                CategoryListVo categoryVo = new CategoryListVo();
                categoryVo.setId(category.getId() + "");
                categoryVo.setName(category.getName());
                categoryVo.setIdx(category.getIdx());
                resultList.add(categoryVo);
            }
        }
        return resultList;
    }

    public List<CategoryAndBrandVo> findAllCategory() {
        List<CategoryAndBrandVo> categoryAndBrandVos = Lists.newArrayList();
        List<Category> categorys = categoryRepository.findAll();
        for (Category category : categorys) {
            CategoryAndBrandVo categoryAndBrandVo = new CategoryAndBrandVo();
            categoryAndBrandVo.setBbcCategoryId(category.getId());
            categoryAndBrandVo.setBbcCategoryName(category.getName());

            categoryAndBrandVos.add(categoryAndBrandVo);
        }

        return categoryAndBrandVos;
    }

    /**
     * 分类列表页分类展示
     * 在App前端展示分类品牌页面的分类信息,listAppCategoryBySort sort set需要手动构造
     */
    public List<IdNameVo> findAllCategoryForApp(){
        List<CategoryRo> categoryRos = categoryRedisDao.listAppCategoryBySort();
        List<IdNameVo> respVos = Lists.newArrayList();
        for (CategoryRo categoryRo : categoryRos) {
            if (!IStatus.DELETE.equals(categoryRo.getStatus())){
                respVos.add(new IdNameVo(String.valueOf(categoryRo.getId()),categoryRo.getName()));

            }
        }
        return respVos;
    }

}
